﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using BusinessLayer.EnumsAndContainers;
using BusinessLayer.Objects;
using BusinessLayer.Tools;
using Deployment.Tools;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;

namespace Deployment
{
    /// <summary>
    /// Main form class
    /// </summary>
    public partial class MainForm : Form
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="MainForm"/> class.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Returns all checkboxes that represent projects that are avaliable for current target.
        /// </summary>
        private IEnumerable<CheckBox> AvailableSites
        {
            get
            {
                var items = new List<CheckBox>();
                foreach (Control control in gbxSites.Controls)
                {
                    var checkBox = control as CheckBox;
                    if (checkBox != null && checkBox.Enabled)
                    {
                        items.Add(checkBox);
                    }
                }
                return items;
            }
        }

        /// <summary>
        /// Returns current build configuration name.
        /// </summary>
        private string BuildConfiguration
        {
            get
            {
                var selectedTarget = ((KeyValuePair<int, string>) cbxTarget.SelectedValue).Key;
                return EnumsExtender.GetConfiguration((TargetSystem) selectedTarget);
            }
        }

        /// <summary>
        /// Returns list of all projects selected for deployment.
        /// </summary>
        private List<DeployableProject> DeployableProjects
        {
            get
            {
                var siteNames = AvailableSites.Where(c => c.Checked).Select(c => c.Text).ToList();
                var selectedTarget = ((KeyValuePair<int, string>) cbxTarget.SelectedValue).Key;
                var projects = DeployableProjectsContainer.GetProjectsInformation((TargetSystem) selectedTarget,
                    SolutionPath);
                var returnValue = siteNames.Select(s => projects.First(p => p.ProjectName == s)).ToList();
                return returnValue;
            }
        }

        /// <summary>
        /// Returns absolute path to logs folder.
        /// </summary>
        private string LogsPath
        {
            get
            {
                return PathesHelper.FixLogsPath(txtLogsFolder.Text, false);
            }
        }

        /// <summary>
        /// Returns absolute path to solution folder.
        /// </summary>
        private string SolutionPath
        {
            get
            {
                return PathesHelper.FixSolutionPath(txtSolutionFolder.Text);
            }
        }

        internal void MainForm_Load(object sender, EventArgs e)
        {
            var dataSource = new Dictionary<int, string>
            {
                {(int) TargetSystem.Test, EnumsExtender.GetDescription(TargetSystem.Test)},
                {(int) TargetSystem.AzureERP, EnumsExtender.GetDescription(TargetSystem.AzureERP)},
                //{(int) TargetSystem.AzureCT, EnumsExtender.GetDescription(TargetSystem.AzureCT)}
            };
            SetProgressStatus("Waiting for start", Color.Black, false);
            btnCancelDeploy.Enabled = false;
            cbxTarget.DataSource = new BindingSource(dataSource, null);
            cbxTarget.DisplayMember = "Value";
            txtSolutionFolder.Text = Path.GetDirectoryName(Application.ExecutablePath);
        }

        #region Private methods

        private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int success = 0;
            int failure = 0;
            var worker = (BackgroundWorker) sender;
            var input = (BackgroundWorkerInputData) e.Argument;
            var deployableProjects = input.DeployableProjects;
            var solutionPath = input.SolutionPath;
            var logsPath = input.LogsPath;
            var buildConfiguration = input.BuildConfiguration;
            var count = deployableProjects.Count;
            for (int i = 0; i < count; i++)
            {
                if (worker.CancellationPending)
                {
                    bgw.ReportProgress(100,
                        new BackgroundWorkerReport
                        {
                            IsTotal = true,
                            Success = success,
                            Failure = failure,
                            Skipped = (count - success - failure),
                            Aborted = true
                        });
                    return;
                }
                var persentage = (((i*100/count)));
                bgw.ReportProgress(persentage, new BackgroundWorkerReport {IsStartProject = true});
                var initialText = string.Format("==============Project {0} ({1} of {2})==============",
                    deployableProjects[i].ProjectName, i + 1, deployableProjects.Count());
                bgw.ReportProgress(persentage, new BackgroundWorkerReport {Text = initialText, AddSpace = false});

                var projectDescription = string.Format("Project path: {0}; publish profile: {1}, configuration: {2}",
                    deployableProjects[i].ProjectPath, deployableProjects[i].PublishProfile, buildConfiguration);
                bgw.ReportProgress(persentage, new BackgroundWorkerReport {Text = projectDescription, AddSpace = false});
                var pc = new ProjectCollection();
                var buildParameters = new BuildParameters(pc);
                string logFilePath = string.Format(@"{0}{1}_{2}_DeployLog.log",
                    logsPath, DateTime.Now.ToString("yy.MM.dd_HH.mm.ss"), deployableProjects[i].ProjectName);
                bgw.ReportProgress(persentage,
                    new BackgroundWorkerReport
                    {
                        Text = string.Format("Log will be written to file: {0}", logFilePath),
                        AddSpace = true
                    });
                var fileLogger = new FileLogger
                {
                    Parameters = string.Format("logfile={0}", logFilePath)
                };
                var consoleLogger = new ConsoleLogger();
                buildParameters.Loggers = new List<ILogger> {fileLogger, consoleLogger};
                var globalProperty = new Dictionary<string, string>
                {
                    {"Configuration", buildConfiguration},
                    {"DeployOnBuild", "true"},
                    {"PublishProfile", deployableProjects[i].PublishProfile},
                    {"SolutionDir", solutionPath},
                    {"UserName", deployableProjects[i].UserName},
                    {"Password", deployableProjects[i].Password}
                };

                var buildRequest = new BuildRequestData(deployableProjects[i].ProjectPath, globalProperty, null,
                    new[] {"Build"}, null);

                try
                {
                    BuildResult buildResult = BuildManager.DefaultBuildManager.Build(buildParameters, buildRequest);
                    var result = buildResult.OverallResult == BuildResultCode.Success;
                    if (result)
                    {
                        success++;
                    }
                    else
                    {
                        failure++;
                    }
                    bgw.ReportProgress(persentage,
                        new BackgroundWorkerReport
                        {
                            Text = result ? "Operation success" : "Operation fails",
                            IsColored = true,
                            Color = result ? Color.Green : Color.Red
                        });
                }
                catch (Exception ex)
                {
                    bgw.ReportProgress(persentage,
                        new BackgroundWorkerReport
                        {
                            Text = string.Format("Operation fails with exception: {0}", ex.Message),
                            IsColored = true,
                            Color = Color.Red
                        });
                    failure++;
                }
            }
            bgw.ReportProgress(100,
                new BackgroundWorkerReport {IsTotal = true, Success = success, Failure = failure, Skipped = 0});
        }

        private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            var report = (BackgroundWorkerReport) e.UserState;
            prBar.Value = e.ProgressPercentage;
            SetProgressStatus(string.Format("Completed {0}%. Working...", e.ProgressPercentage), Color.Green, false);
            if (report.IsStartProject)
            {
                if (txtResults.Lines.Length > 0)
                {
                    SetText(string.Empty, false);
                }
                return;
            }
            if (!report.IsTotal)
            {
                if (!report.IsColored)
                {
                    SetText(report.Text, report.AddSpace);
                    return;
                }
                SetColoredText(report.Color, report.Text);
                return;
            }
            SetTotalResults(report.Success, report.Failure, report.Skipped);
            SetProgressStatus("Operations success", Color.Green, true);
            if (report.Aborted)
            {
                SetText(string.Empty, true);
                SetColoredText(Color.Red, "Aborted by user!!");
                SetProgressStatus("Aborted by user", Color.Red, true);
            }
        }

        private void BackgroundWorker_RunWorkerCompleted(object sender,
            RunWorkerCompletedEventArgs e)
        {
            btnCancelDeploy.Enabled = false;
        }

        private void BtnCancelDeploy_Click(object sender, EventArgs e)
        {
            if (bgw.IsBusy)
            {
                bgw.CancelAsync();
                SetProgressStatus("Canceling...", Color.Red, true);
            }
        }

        private void BtnDeploy_Click(object sender, EventArgs e)
        {
            if (bgw.IsBusy)
            {
                MessageBoxEx.Show(this,
                    "Operation already started. Wait for it to complete or stop current by pressing Cancel button in the bottom",
                    "Already started.",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            if (!CheckInputParameters()) return;
            var messageBoxMessage =
                string.Format("Before processing, please, check input data\nSure want to process deploy?");
            if (MessageBoxEx.Show(this, messageBoxMessage, "Confirmation",
                MessageBoxButtons.YesNo, MessageBoxIcon.Information) !=
                DialogResult.Yes)
            {
                return;
            }
            btnCancelDeploy.Enabled = true;
            prBar.Value = 0;
            var inputData = new BackgroundWorkerInputData
            {
                DeployableProjects = DeployableProjects,
                LogsPath = LogsPath,
                SolutionPath = SolutionPath,
                BuildConfiguration = BuildConfiguration
            };
            bgw.RunWorkerAsync(inputData);
        }

        private void BtnDeselectAll_Click(object sender, EventArgs e)
        {
            foreach (var checkBox in AvailableSites)
            {
                checkBox.Checked = false;
            }
        }

        private void BtnFindSolutionFolder_Click(object sender, EventArgs e)
        {
            if (dlgFolderBrowserDialog.ShowDialog(this) == DialogResult.OK)
            {
                txtSolutionFolder.Text = dlgFolderBrowserDialog.SelectedPath;
            }
        }

        private void BtnInvertSelection_Click(object sender, EventArgs e)
        {
            foreach (var checkBox in AvailableSites)
            {
                checkBox.Checked = !checkBox.Checked;
            }
        }

        private void BtnSelectAll_Click(object sender, EventArgs e)
        {
            foreach (var checkBox in AvailableSites)
            {
                checkBox.Checked = true;
            }
        }

        private void BtnSetLogsFolder_Click(object sender, EventArgs e)
        {
            if (dlgFolderBrowserDialog.ShowDialog(this) == DialogResult.OK)
            {
                txtLogsFolder.Text = dlgFolderBrowserDialog.SelectedPath;
            }
        }

        private void CbxTarget_SelectedIndexChanged(object sender, EventArgs e)
        {
            var selectedItemKey = ((KeyValuePair<int, string>) cbxTarget.SelectedValue).Key;
            if (selectedItemKey != (int) TargetSystem.Test)
            {
                chbxCustomerSTSWebSite.Checked =
                    chbxISOSTSWebSite.Checked =
                        chbxMerchantSTSWebSite.Checked =
                            chbxPinSTSWebSite.Checked =
                                chbxCustomerSTSWebSite.Enabled =
                                    chbxISOSTSWebSite.Enabled =
                                        chbxMerchantSTSWebSite.Enabled = chbxPinSTSWebSite.Enabled = false;
            }
            else
            {
                chbxCustomerSTSWebSite.Enabled =
                    chbxISOSTSWebSite.Enabled = chbxMerchantSTSWebSite.Enabled = chbxPinSTSWebSite.Enabled = true;
            }
        }

        private bool CheckInputParameters()
        {
            if (!DeployableProjects.Any())
            {
                MessageBoxEx.Show(this, "Select at least one project", "Failed",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            if (string.IsNullOrEmpty(SolutionPath))
            {
                MessageBoxEx.Show(this, "Solution folder should be specified", "Failed",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            if (!Directory.Exists(SolutionPath))
            {
                MessageBoxEx.Show(this, "Specified solution folder does not exist", "Failed",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            if (string.IsNullOrEmpty(LogsPath))
            {
                MessageBoxEx.Show(this, "Logs folder should be specified", "Failed",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            txtResults.Clear();
            if (!Directory.Exists(LogsPath))
            {
                SetColoredText(Color.Red, string.Format("Directory {0} does not exist. Trying to create.", LogsPath));
                try
                {
                    Directory.CreateDirectory(LogsPath);
                }
                catch (Exception ex)
                {
                    SetColoredText(Color.Red, string.Format("Error creating directory!! Message: {0}", ex.Message));
                    return false;
                }
                SetColoredText(Color.Green, "Directory successfully created.");
            }
            return true;
        }

        private void SetColoredText(Color color, string text)
        {
            int start = txtResults.TextLength;
            txtResults.AppendText(text);
            int end = txtResults.TextLength;

            // Text box may transform chars, so (end-start) != text.Length
            txtResults.Select(start, end - start);
            {
                txtResults.SelectionColor = color;
                // could set box.SelectionBackColor, box.SelectionFont too.
            }
            txtResults.SelectionLength = 0; // clear
            txtResults.AppendText(Environment.NewLine);
        }

        private void SetInlineResultsText(Color color, string text)
        {
            var start = txtResults.TextLength;
            txtResults.AppendText(text);
            var end = txtResults.TextLength;
            txtResults.Select(start, end - start);
            {
                txtResults.SelectionColor = color;
                // could set box.SelectionBackColor, box.SelectionFont too.
            }
            txtResults.SelectionLength = 0;
        }

        private void SetProgressStatus(string text, Color color, bool bold)
        {
            lblProgressStatus.Font = new Font(lblProgressStatus.Font, bold ? FontStyle.Bold : FontStyle.Regular);
            lblProgressStatus.ForeColor = color;
            lblProgressStatus.Text = text;
        }

        private void SetText(string text, bool addSpace)
        {
            var textToAppend = string.Concat(text, Environment.NewLine, addSpace ? Environment.NewLine : string.Empty);
            txtResults.AppendText(textToAppend);
        }

        private void SetTotalResults(int success, int failure, int skipped)
        {
            int start = txtResults.TextLength;
            txtResults.AppendText(Environment.NewLine + Environment.NewLine +
                                  "==============Overall results==============" + Environment.NewLine);
            int end = txtResults.TextLength;

            // Text box may transform chars, so (end-start) != text.Length
            txtResults.Select(start, end - start);
            {
                txtResults.SelectionColor = Color.Blue;
                // could set box.SelectionBackColor, box.SelectionFont too.
            }
            txtResults.SelectionLength = 0; // clear
            txtResults.AppendText(Environment.NewLine);


            SetInlineResultsText(Color.Blue, "Deployment: ");
            SetInlineResultsText(Color.Green, string.Format("{0} succeeded; ", success));
            SetInlineResultsText(Color.Red, string.Format("{0} failed; ", failure));
            SetInlineResultsText(Color.Gray, string.Format("{0} skipped; ", skipped));
            SetInlineResultsText(Color.Blue, string.Format("Completed at {0}", DateTime.Now.ToString("U")));

        }

        private void TxtResults_TextChanged(object sender, EventArgs e)
        {
            txtResults.SelectionStart = txtResults.Text.Length;
            txtResults.ScrollToCaret();
            txtResults.SelectionLength = 0;
        }

        private void TxtSolutionFolder_TextChanged(object sender, EventArgs e)
        {
            var fixedPath = PathesHelper.FixSolutionPath(txtSolutionFolder.Text);
            txtLogsFolder.Text = PathesHelper.FixLogsPath(fixedPath, true);
        }

        #endregion
    }
}
